package main

import (
	"context"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"strconv"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"github.com/go-redis/redis/v8"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type Essay struct {
	ID         uint   `gorm:"primaryKey" json:"id"`
	Type       string `gorm:"column:type;not null;default:'undefined'" json:"type"`
	Author     string `gorm:"column:author;not null" json:"author"`
	Time       string `gorm:"column:time;not null" json:"time"`
	Title      string `gorm:"column:title;not null" json:"title"`
	Content    string `gorm:"column:content;not null" json:"content"`
	Attachment string `gorm:"column:attachment" json:"attachment"`
	CoverUrl   string `gorm:"column:coverurl" json:"coverurl"`
	Intro      string `gorm:"column:Intro;not null" json:"Intro"`
}

type User struct {
	ID        uint   `gorm:"primaryKey"`
	Username  string `gorm:"column:username;unique;not null"`
	Password  string `gorm:"column:password;not null"`
	UserGroup string `gorm:"column:user_group;default:'undefined'"`
	Status    string `gorm:"column:status;default:'guest'"`
	Level     uint   `gorm:"column:level;default:0"`
}

type Leader struct {
	ID     uint   `gorm:"primaryKey" json:"id"`
	Header string `gorm:"header" json:"header"`
	Url    string `gorm:"url" json:"url"`
	Footer string `gorm:"footer" json:"footer"`
}

type RegisterBody struct {
	Username string `json:"hbjtusername"`
	Password string `json:"hbjtpassword"`
}

type LoginBody struct {
	Username string `json:"hbjtusername"`
	Password string `json:"hbjtpassword"`
}

const (
	// 设置密钥，用于加密和解密JWT
	secretKey        = "hbjt"
	IP               = "127.0.0.1"
	PORT             = ":35802"
	MaxLoginAttempts = 3
	LockoutDuration  = 5 * time.Minute
)

var db *gorm.DB

func UploadHandler(c *gin.Context) {
	// 4Mb
	err := c.Request.ParseMultipartForm(10 << 20)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"Errno":   1,
			"Message": "尼玛个比的，文件太他妈大了",
		})
		return
	}
	file, err := c.FormFile("upload")
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"Errno":   1,
			"Message": err.Error(),
		})
		return
	}
	filename := strconv.FormatInt(time.Now().Unix(), 10) + "-" + file.Filename
	dir := "static/upload/"
	filePath := dir + filename
	os.MkdirAll(dir, 0777)
	if err := c.SaveUploadedFile(file, filePath); err != nil {
		// 处理保存文件失败的情况
		c.JSON(http.StatusBadRequest, gin.H{
			"Errno":   1,
			"Message": err.Error(),
		})
		return
	}
	// 拼接url
	url := "http://" + IP + PORT + "/" + filePath
	c.JSON(http.StatusOK, gin.H{
		"Errno": 0,
		"data": gin.H{
			"url": url,
		},
	})
}

// 解析和验证JWT
func ParseAndValidateJWT(tokenString string) error {
	// 解析JWT令牌
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		// 验证签名的方法
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
		}

		// 返回密钥
		return []byte(secretKey), nil
	})

	if err != nil {
		return err
	}

	// 验证令牌是否有效
	if _, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		return nil
	} else {
		return fmt.Errorf("invalid token")
	}
}

func adminMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		userID := c.MustGet("user_id").(string)

		var me User
		result := db.Where(&User{Username: userID}).Find(&me)
		if result.Error != nil {
			c.JSON(http.StatusNotAcceptable, gin.H{"message": "No User"})
			c.Abort()
			return
		}

		if me.Status != "admin" && me.Status != "super_admin" {
			c.JSON(http.StatusNotAcceptable, gin.H{"message": "No Permission"})
			c.Abort()
			return
		}

		c.Next()
	}
}

func superAdminMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		userID := c.MustGet("user_id").(string)

		var me User
		result := db.Where(&User{Username: userID}).Find(&me)
		if result.Error != nil {
			c.JSON(http.StatusNotAcceptable, gin.H{"message": "No User"})
			c.Abort()
			return
		}

		if me.Status != "super_admin" {
			c.JSON(http.StatusNotAcceptable, gin.H{"message": "No Permission"})
			c.Abort()
			return
		}

		c.Next()
	}
}

// 定义中间件函数
func JWTMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 获取Authorization头中的Token
		authHeader := c.GetHeader("Authorization")
		if authHeader == "" {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header missing"})
			c.Abort()
			return
		}

		// 检查Token的格式
		var tokenString string
		_, err := fmt.Sscanf(authHeader, "Bearer %s", &tokenString)
		if err != nil {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token format"})
			c.Abort()
			return
		}

		// 解析和验证Token
		token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
			// 验证签名的方法
			if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
				return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
			}

			// 返回密钥
			return []byte(secretKey), nil
		})

		if err != nil || !token.Valid {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
			c.Abort()
			return
		}

		// 将解析后的Token信息存储在上下文中，以便后续处理程序使用
		claims := token.Claims.(jwt.MapClaims)
		c.Set("user_id", claims["user_id"])

		// 继续处理下一个中间件或处理程序
		c.Next()
	}
}

// 中间件：限制登录失败次数
func LimitLoginAttempts(client *redis.Client) gin.HandlerFunc {
	return func(c *gin.Context) {
		// 获取请求中的用户名
		username := c.PostForm("username")

		// 根据用户名构建 Redis 键名
		key := fmt.Sprintf("login_attempts:%s", username)

		// 获取当前登录失败次数
		count, err := client.Get(context.Background(), key).Int()
		if err != nil && err != redis.Nil {
			c.AbortWithStatus(500)
			return
		}

		// 如果登录失败次数超过阈值，则拒绝服务
		if count >= MaxLoginAttempts {
			c.JSON(403, gin.H{"error": "Too many login attempts. Please try again later."})
			c.Abort()
			return
		}

		// 执行下一个处理程序
		c.Next()

		// 登录失败时增加计数
		if c.Writer.Status() == 401 {
			// 增加计数并设置过期时间
			client.Incr(context.Background(), key)
			client.Expire(context.Background(), key, LockoutDuration)
		}
	}
}

// 生成JWT
func GenerateJWT(user_id string) (string, error) {
	// 创建一个新的令牌
	token := jwt.New(jwt.SigningMethodHS256)

	// 设置令牌的声明，可以自定义一些字段
	claims := token.Claims.(jwt.MapClaims)
	claims["user_id"] = user_id

	// 使用密钥对令牌进行签名
	tokenString, err := token.SignedString([]byte(secretKey))
	if err != nil {
		return "", err
	}

	return tokenString, nil
}

func AdminLoginHandler(c *gin.Context) {
	var loginBody LoginBody
	// 将请求体绑定到结构体
	if err := c.ShouldBindJSON(&loginBody); err != nil {
		log.Println("Failed to bind request body:", err)
		c.JSON(400, gin.H{"error": "Bad Request"})
		return
	}

	var users []User
	result := db.Where(&User{Username: loginBody.Username}).Find(&users)
	if result.Error != nil {
		panic("Failed to query users")
	}

	if len(users) == 0 {
		c.JSON(http.StatusForbidden, gin.H{"message": "Cannot find user"})
		return
	}

	err := bcrypt.CompareHashAndPassword([]byte(users[0].Password), []byte(loginBody.Password))
	if err != nil {
		c.JSON(http.StatusUnauthorized, gin.H{"message": "Wrong password"})
		return
	}

	if users[0].Status != "admin" && users[0].Status != "super_admin" {
		c.JSON(http.StatusUnauthorized, gin.H{"message": "No Permission"})
		return
	}

	val, _ := GenerateJWT(loginBody.Username)
	c.JSON(http.StatusOK, gin.H{"message": "Login success", "token": val})
}

func UserLoginHandler(c *gin.Context) {
	var loginBody LoginBody
	// 将请求体绑定到结构体
	if err := c.ShouldBindJSON(&loginBody); err != nil {
		log.Println("Failed to bind request body:", err)
		c.JSON(400, gin.H{"error": "Bad Request"})
		return
	}

	var users []User
	result := db.Where(&User{Username: loginBody.Username}).Find(&users)
	if result.Error != nil {
		panic("Failed to query users")
	}

	if len(users) == 0 {
		c.JSON(http.StatusForbidden, gin.H{"message": "Cannot find user"})
		return
	}

	err := bcrypt.CompareHashAndPassword([]byte(users[0].Password), []byte(loginBody.Password))
	if err != nil {
		c.JSON(http.StatusUnauthorized, gin.H{"message": "Wrong password"})
		return
	}

	val, _ := GenerateJWT(loginBody.Username)
	c.JSON(http.StatusOK, gin.H{"message": "Login success", "token": val})
}

func RegisterHandler(c *gin.Context) {
	var registerBody RegisterBody
	// 将请求体绑定到结构体
	if err := c.ShouldBindJSON(&registerBody); err != nil {
		log.Println("Failed to bind request body:", err)
		c.JSON(http.StatusBadRequest, gin.H{"error": "Bad Request"})
		return
	}

	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(registerBody.Password), bcrypt.DefaultCost)
	if err != nil {
		log.Fatal(err)
	}

	user := User{
		Username: registerBody.Username,
		Password: string(hashedPassword),
	}
	result := db.Create(&user)
	if result.Error != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Bad Request"})
		return
	}

	val, _ := GenerateJWT(registerBody.Username)

	c.JSON(http.StatusOK, gin.H{"message": "Register success", "token": val})
}

func AddEssayHandler(c *gin.Context) {
	userID := c.MustGet("user_id").(string)

	var essay Essay
	if err := c.ShouldBindJSON(&essay); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	essay.Author = userID
	essay.Time = time.Now().In(time.Local).Format("2006-01-02 15:04:05")

	result := db.Create(&essay)
	if result.Error != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Bad Request"})
	}

	c.JSON(http.StatusOK, gin.H{"message": "addEssay success"})
}

type DelUserBody struct {
	Username string `json:"username"`
}

func DelUserHandler(c *gin.Context) {
	var delUserBody DelUserBody
	if err := c.ShouldBindJSON(&delUserBody); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	result := db.Where("username = ?", delUserBody.Username).Delete(&User{})

	if result.Error != nil {
		c.JSON(http.StatusNotFound, gin.H{"message": "db wrong"})
	}

	c.JSON(http.StatusOK, gin.H{"message": "Del Success"})
}

type UpdateUserStatusBody struct {
	Username  string `json:"username"`
	Status    string `json:"status"`
	UserGroup string `json:"user_group"`
	Level     uint   `json:"level"`
}

func UpdateUserInfoHandler(c *gin.Context) {
	var UpdateUserStatusBody UpdateUserStatusBody
	if err := c.ShouldBindJSON(&UpdateUserStatusBody); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	db.Model(&User{}).Where("username = ?", UpdateUserStatusBody.Username).Updates(User{Status: UpdateUserStatusBody.Status, UserGroup: UpdateUserStatusBody.UserGroup, Level: UpdateUserStatusBody.Level})
	c.JSON(http.StatusOK, gin.H{"message": "Update Success"})
}

type DelEssayBody struct {
	ID uint `json:"id"`
}

func DelEssayHandler(c *gin.Context) {
	var delEssayBody DelEssayBody
	if err := c.ShouldBindJSON(&delEssayBody); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	db.Where("id = ?", delEssayBody.ID).Delete(&Essay{})
	c.JSON(http.StatusOK, gin.H{"message": "Del Success"})
}

func GetAllUserHandler(c *gin.Context) {
	userID := c.MustGet("user_id").(string)

	var me User
	result := db.Where(&User{Username: userID}).Find(&me)
	if result.Error != nil {
		c.JSON(http.StatusNotAcceptable, gin.H{"message": "No User"})
		c.Abort()
		return
	}

	if me.Status == "super_admin" {
		var users []User
		db.Find(&users)
		c.JSON(http.StatusOK, gin.H{"message": "Get All User Success", "data": users})
		return
	} else {
		var users []User
		result := db.Where("user_group = ?", me.UserGroup).Find(&users)
		if result.Error != nil {
			c.JSON(http.StatusNotFound, gin.H{"message": "wrong db", "error": result.Error})
			return
		}
		c.JSON(http.StatusOK, gin.H{"message": "Get All User Success", "data": users})
		return
	}
}

func GetAllEssayHandler(c *gin.Context) {
	var essays []Essay
	result := db.Find(&essays)
	if result.Error != nil {
		c.JSON(http.StatusNotFound, gin.H{"message": "wrong db"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "Get All Essay Success", "data": essays})
}

func GetEssayByTypeHandler(c *gin.Context) {
	Type := c.Query("type")
	var essays []Essay
	db.Where("type = ?", Type).Find(&essays)

	c.JSON(http.StatusOK, gin.H{"message": "Get Essay By Type Success", "data": essays})
}

func GetEssayByIdHandler(c *gin.Context) {
	ID := c.Query("id")
	var essay Essay
	result := db.First(&essay, ID)
	if result.Error != nil {
		c.JSON(http.StatusNotFound, gin.H{"message": "wrong db"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "Get Essay By ID Success", "data": essay})
}

func UpdateEssayByIdHandler(c *gin.Context) {
	var req Essay
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	db.Model(&Essay{}).Where("id = ?", req.ID).Updates(Essay{Title: req.Title, Content: req.Content, Attachment: req.Attachment, CoverUrl: req.CoverUrl, Intro: req.Intro})
	c.JSON(http.StatusOK, gin.H{"message": "Update Success"})
}

func AddLeaderHandler(c *gin.Context) {
	var req Leader
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	result := db.Create(&req)
	if result.Error != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Wrong db"})
	}

	c.JSON(http.StatusOK, gin.H{"message": "addLeader success"})
}

func getAllLeaderHandler(c *gin.Context) {
	var leaders []Leader
	result := db.Find(&leaders)
	if result.Error != nil {
		c.JSON(http.StatusNotFound, gin.H{"message": "wrong db"})
		return
	}
	c.JSON(http.StatusOK, gin.H{"message": "Get All Leader Success", "data": leaders})
}

type DelLeaderBody struct {
	ID uint `json:"id"`
}

func DelLeaderByIdHandler(c *gin.Context) {
	var req DelLeaderBody
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	db.Where("id = ?", req.ID).Delete(&Leader{})
	c.JSON(http.StatusOK, gin.H{"message": "Del Success"})
}

func UpdateLeaderInfoHandler(c *gin.Context) {
	var req Leader
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	db.Model(&Leader{}).Where("id = ?", req.ID).Updates(Leader{Header: req.Header, Url: req.Url, Footer: req.Footer})
	c.JSON(http.StatusOK, gin.H{"message": "Update Success"})
}

func connect() *redis.Client {
	// 创建Redis客户端
	client := redis.NewClient(&redis.Options{
		Addr:     "localhost:16379", // Redis服务器地址和端口
		Password: "",                // 密码（如果设置了密码）
		DB:       0,                 // 数据库索引
	})

	// Ping测试连接
	_, err := client.Ping(context.Background()).Result()
	if err != nil {
		panic(err)
	}

	return client
}

func main() {
	dsn := "root:123456@tcp(127.0.0.1:3406)/mysql?parseTime=true"
	var err error
	db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic(err)
	}
	// 自动迁移模型
	err = db.AutoMigrate(&Essay{})
	if err != nil {
		panic(err)
	}
	err = db.AutoMigrate(&User{})
	if err != nil {
		panic(err)
	}
	err = db.AutoMigrate(&Leader{})
	if err != nil {
		panic(err)
	}
	// 创建日志文件
	file, err := os.Create("gin.log")
	if err != nil {
		fmt.Println("Failed to create log file:", err)
		return
	}
	defer file.Close()

	// 设置Gin的日志输出为文件
	gin.DefaultWriter = io.MultiWriter(file, os.Stdout)
	// gin.SetMode(gin.ReleaseMode)

	router := gin.Default()

	config := cors.DefaultConfig()
	config.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type", "Authorization"}
	config.AllowOrigins = []string{"*"}

	router.Use(cors.New(config))
	router.POST("/api/register", RegisterHandler)
	router.GET("/api/getEssayByType", GetEssayByTypeHandler)
	router.GET("/api/getEssayById", GetEssayByIdHandler)
	router.GET("/api/getAllLeader", getAllLeaderHandler)
	router.Static("/static/upload", "./static/upload/")

	// 登陆过多拒绝服务
	loginGroup := router.Group("/")
	client := connect()
	loginGroup.Use(LimitLoginAttempts(client))
	loginGroup.POST("/api/admin/login", AdminLoginHandler)
	loginGroup.POST("/api/user/login", UserLoginHandler)

	tokenGroup := router.Group("/")
	tokenGroup.Use(JWTMiddleware())
	tokenGroup.GET("/api/getAllEssay", GetAllEssayHandler)

	adminGroup := tokenGroup.Group("/")
	adminGroup.Use(adminMiddleware())
	adminGroup.PUT("/api/updateEssayById", UpdateEssayByIdHandler)
	adminGroup.POST("/api/upload", UploadHandler)
	adminGroup.POST("/api/addEssay", AddEssayHandler)
	adminGroup.DELETE("/api/delEssay", DelEssayHandler)
	adminGroup.GET("/api/getAllUser", GetAllUserHandler)

	superAdminGroup := tokenGroup.Group("/")
	superAdminGroup.Use(superAdminMiddleware())
	superAdminGroup.PUT("/api/updateUserInfo", UpdateUserInfoHandler)
	superAdminGroup.PUT("/api/updateLeaderInfo", UpdateLeaderInfoHandler)
	superAdminGroup.DELETE("/api/delUser", DelUserHandler)
	superAdminGroup.DELETE("/api/delLeader", DelLeaderByIdHandler)
	superAdminGroup.POST("/api/addLeader", AddLeaderHandler)
	router.Run(PORT)
}

